<?php
namespace src\logic;
use classlib\input;
use \classlib\core as C;
class api_rtmp {
	const push_uri  = 'rtmp://%s/push_%s?ptoken=%s%08d?vhost=vpush/%d_%d';
	const live_uri  = 'rtmp://%s/livepkgr?rtoken=%s%08d/%d_%d';
	const copy_uri  = 'rtmp://%s/livepkgr?rtoken=%s%08d&lp_origin=%s/%d_%d';
	const near_uri  = 'rtmp://%s%s?rtoken=%s%08d?vhost=vpush/%d_%d';
	const near_psh  = 'rtmp://%s%s?ptoken=%s%08d?vhost=vpush/%d_%d';
	const area_def  = 1;
	static $area_cnf=array(1=>'北美[NORTHAMERICA]', 2=>'亚洲[ASIA]', 4=>'欧洲[EUROPE]');

	/**
	 * 根据IP进行地理定位 获取国家编码
	 * @param unknown $ip
	 * @return string
	 */
	private static function ip2Location($ip) {
		require_once COREDIR.'/classlib/plugins/_ip2location/IP2Location.php';
		$fileio = COREDIR.'/IP2LOCATION-LITE-DB11.BIN/IP2LOCATION-LITE-DB11.BIN';
		$db     = new \IP2Location\Database($fileio, \IP2Location\Database::FILE_IO);
		$fields = array(\IP2Location\Database::COUNTRY_CODE);
		$records= $db->lookup($ip, $fields);
		if ($records && isset($records['countryCode']) && $records['countryCode'] != '-') {
			$code = $records['countryCode'];
		} else {
			$code = self::area_def;
		}
		return $code;
	}

	/**
	 * 定位hls地址
	 * @param unknown $evtid
	 * @param unknown $userid
	 * @return string
	 */
	public static function hls($evtid, $userid) {
		$static = lconfig::str('system_vodstatic_uri');
		$prefix = sprintf('%04x', ($userid + $evtid)%65536);
		$hlsseg = (C\config::env() == 'pro')? 'hls':'hls_qa';
		$hls    = sprintf('%s/%s/%s/m3u8/%d/%d/%d_%d_001_hls.m3u8',
				$static, $prefix, $hlsseg, $userid, $evtid, $userid, $evtid);
		return $hls;
	}

	/**
	 * 通过IP定位当前请求分配到的区域机房
	 * @return number
	 */
	public static function get_area_code() {
		if (isset($_COOKIE['crosssite']) && isset(self::$area_cnf[$_COOKIE['crosssite']])) {
			return intval($_COOKIE['crosssite']);
		}
		$ip    = C\get_ip();
		$ckey  = 'iparcode_'.$ip;
		$cache = \classlib\cache::inst('file');
		$arcode= $cache->get($ckey);
		if (!$arcode || !is_numeric($arcode)) {
			$ccode = self::ip2Location($ip);
			$hash  = input::strhash($countrycode);
			$where = array('area2int'=>$hash, 'area'=>$countrycode);
			$arcode= C\model::load('pro.inc.region')->get_item($where);
			$arcode= empty($arcode)? self::area_def:$arcode['region'];
			$cache->set($ckey, $arcode, 86400);
		}
		setcookie('crosssite', $arcode, 0, '/');
		return intval($arcode);
	}

	/**
	 * 获取RMTP地址信息
	 * @param unknown $stype
	 * @param unknown $region
	 * @param unknown $sorts
	 */
	private static function __rtmp($stype, $region, $sorts=null) {
		$where = array('stype'=>$stype, 'status'=>1, 'region'=>$region);
		(!$sorts) && $sorts = array('rank'=>\classlib\orm::ASC);
		$fields= 'id,pid,limitfd,rtmp,host,ports';
		$list  = C\model::load('pro.inc.rtmp')->get_list(0, -1, $where, $fields, $sorts);
		return $list;
	}

	/**
	 * 获取测速地址信息列表 最多三条
	 * @return array
	 */
	public static function speed() {
		$region = self::get_area_code();
		$list   = array();
		$data   = self::__rtmp(3, $region);
		if ($data) {
			foreach ($data as &$item) {
				$ports = @explode(',', $item['ports']);
				$tpl   = 'rtmp://%s:%d/live?key=35c9b402c12a7246868752e2878f7e0e&vhost=bandcheck.srs.com';
				foreach ($ports as $port) {
					$list[] = sprintf($tpl, $item['rtmp'], $port);
				}
			}
		}
		return $list;
	}

	/**
	 * OBS网络检查
	 * @return string[]
	 */
	public static function speedobs() {
		$region = self::get_area_code();
		$data   = self::__rtmp(4, $region);
		$list   = array();
		if ($data) {
			foreach ($data as &$item) {
				$ports  = @explode(',', $item['ports']);
				foreach ($ports as $port) {
					$list[] = sprintf('%s:%d', $item['rtmp'], $port);
				}
			}
		}
		return $list;
	}

	/**
	 * 返回略质服务器同一个ip多个端口的服务
	 * @param unknown $studio
	 * @param unknown $app
	 * @param unknown $evtid
	 * @param string $segs
	 * @return string[]
	 */
	private static function get_casual_push(&$studio, $app, $evtid, $segs='_001') {
		$list = array();
		$data = self::__rtmp(2, $studio['region']);
		if ($data) {
			$idx   = config::$nowtime % count($data);
			$ports = @explode(',', $data[$idx]['ports']);
			foreach ($ports as $port) {
				$clip  = sprintf('%s:%d', $data[$idx]['rtmp'], $port);
				$list[]= sprintf(self::push_uri, $clip, $app, $studio['ptocken'], $studio['id'], $studio['userid'], $evtid).$segs;
			}
		}
		return $list;
	}

	/**
	 * 获取优质服务器的IP端口资料信息
	 * @param unknown $studio
	 * @param unknown $app
	 * @param unknown $evtid
	 * @param string $segs
	 * @return string[]
	 */
	private static function get_orignal_push(&$studio, $app, $evtid, $segs='_001') {
		$list = array();
		$data = self::__rtmp(1, $studio['region']);
		if ($data) {
			$total = count($data);
			$idx   = config::$nowtime % $total;
			for ($loop = 0; $loop < 2; $loop++) {
				$ports = @explode(',', $data[$idx]['ports']);
				foreach ($ports as $port) {
					$clip  = sprintf('%s:%d', $data[$idx]['rtmp'], $port);
					$list[]= sprintf(self::push_uri, $clip, $app, $studio['ptocken'], $studio['id'], $studio['userid'], $evtid).$segs;
				}
				//如果记录数量就一条的话直接终止
				if ($total == 1) {
					break;
				}
				$idx = ($idx + 1) % $total;
			}
		}
		return $list;
	}

	/**
	 * 获取拉流地址信息 返回几条数据
	 * @param string  $app
	 * @param array   $studio
	 * @param int     $evtid
	 * @return array
	 */
	public static function push($app, $studio, $evtid) {
		$list   = array();
		$region = self::get_area_code();
		$studio['region'] = $region;
		($app == 'pc') && $app = 'obs_win';
		if ($studio['iscasual'] == 0) {//获取优质主播的拉流地址
			$list = self::get_orignal_push($studio, $app, $evtid);
		} else {
			$list = self::get_casual_push($studio, $app, $evtid);
		}
		$fields = array('region'=>$region, 'forward'=>$studio['def_forward']);
		C\model::load('pro.pl.studio')->save($studio['id'], $fields);
		return $list;
	}

	/**
	 * 获取vip用户的推流地址
	 * @param unknown $sid
	 * @param unknown $vipid
	 * @param unknown $path
	 * @param number $lcode
	 * @return string[]
	 */
	public static function vip_push($sid, $vipid, $path, $lcode=0) {
		$list   = array();
		$studio = C\model::load('pro.pl.studio')->get_one($sid);
		$segs   = sprintf('_%d%02s', $lcode, dechex($vipid%253+2));
		$scheme = parse_url($studio['rtmp']);
		$where  = array('status'=>1, 'stype'=>array(array(1, 2), orm::OP_IN), 'rtmp'=>$scheme['host']);
		$data   = C\model::load('pro.inc.rtmp')->get_item($where, 'rtmp,host,ports');
		if ($data) {
			$path  = '/push_'.$path;
			$ports = @explode(',', $data['ports']);
			foreach ($ports as $port) {
				$clip  = sprintf('%s:%d', $data['rtmp'], $port);
				$list[]= sprintf(self::near_psh, $clip, $path, $studio['ptocken'], $studio['id'], $studio['userid'], $studio['evtid']).$segs;
			}
		}
		return $list;
	}

	//获取vip最近的拉流地址
	public static function vip_near($sid, $vipid, $path=false, $lcode=0) {
		$list   = array();
		$studio = C\model::load('pro.pl.studio')->get_one($sid);
		$segs   = sprintf('_%d%02s', $lcode, ($vipid > 0)? dechex($vipid%253+2):1);
		$scheme = parse_url($studio['rtmp']);
		$where  = array('status'=>1, 'stype'=>array(array(1, 2), orm::OP_IN), 'rtmp'=>$scheme['host']);
		$data   = C\model::load('pro.inc.rtmp')->get_item($where, 'rtmp,host,ports');
		if ($data) {
			$path  = ($path)? '/push_'.$path:$scheme['path'];
			$ports = @explode(',', $data['ports']);
			foreach ($ports as $port) {
				$clip  = sprintf('%s:%d', $data['rtmp'], $port);
				$list[]= sprintf(self::near_uri, $clip, $path, $studio['rtocken'], $studio['id'], $studio['userid'], $studio['evtid']).$segs;
			}
		}
		return $list;
	}

	/**
	 * 获取vip用户的拉流地址
	 * @param unknown $sid
	 * @param unknown $vipid
	 * @param number $lcode
	 * @return string[]|boolean|unknown
	 */
	public static function vip_recv($sid, $vipid, $lcode=0) {
		$list   = array();
		$studio = C\model::load('pro.pl.studio')->get_one($sid);
		$segs   = sprintf('_%d%02s', $lcode, dechex($vipid%253+2));
		if ($studio['iscasual'] == 0) {//获取优质主播的拉流地址
			$list = self::get_original_recv($studio, $studio['evtid'], $segs);
		} else {
			$list = self::get_casual_recv($studio, $studio['evtid'], $segs);
		}
		return $list;
	}

	/**
	 * 获取推流地址资料信息
	 * @param array $studio
	 * @param int   $evtid
	 */
	private static function get_casual_recv(&$studio, $evtid, $segs='_001') {
		$list = array();
		$areac= self::get_area_code();
		//判断是否是同一个区域 如果是同一个区的知己返回同区域的地址
		if ($areac != $studio['region']) {
			$list = self::get_casual_copy_recv($areac, $evtid, $segs, $studio);
		}
		//如果返回地址信息为空的话 直接返回源地址
		if (empty($list)) {
			$list = self::get_casual_host_recv($evtid, $segs, $studio);
		}
		return $list;
	}

	/**
	 * 获取指定rtmp链路的源服务器
	 * @param unknown $rtmp
	 * @return unknown|boolean|string
	 */
	private static function get_casual_source_origin($rtmp) {
		$ckey  = 'rtmp_srouce_'.md5($rtmp);
		$cache = \classlib\cache::inst('memcached');
		$lptops= $cache->get($ckey);
		if ($lptops) {//缓存存在直接返回
			return $lptops;
		}
		$rtmp  = parse_url($rtmp);
		$rorm  = C\model::load('pro.inc.rtmp');
		$where = array('status'=>1, 'stype'=>2, 'rtmp'=>$rtmp['host']);
		$rtmp  = $rorm->get_item($where, 'pid');
		//因为配置原因查不到数据源的话返回false 然后二次获取同源的服务器资源
		if (!$rtmp || !$rtmp['pid']) {
			return false;
		}
		$source= $rorm->get_one($rtmp['pid']);
		if (!$source || $source['stype'] != 5) {
			return false;
		}
		//更新加密源数据信息
		$lptops= urlencode(base64_encode($source['host'].':'.$source['ports']));
		$cache->set($ckey, $lptops, 86400);
		return $lptops;
	}

	/**
	 * 获取指定区域的镜像，然后配置数据源拉取
	 * @param unknown $areac
	 * @param unknown $evtid
	 * @param unknown $segs
	 * @param unknown $studio
	 * @return unknown|boolean|string[]
	 */
	private static function get_casual_copy_recv($areac, $evtid, $segs, &$studio) {
		//缓存如果存在的话直接通过缓存获取
		$redis = \classlib\cache::inst('redis');
		$ckey  = sprintf('casual_copy_%d_%d_%d', $areac, $evtid, $studio['userid']);
		$csrv  = $redis->get($ckey);
		if ($csrv && is_array($csrv)) {//判断缓存是否存在
			$list  = array();
			$client= (int)$redis->get('state_'.$csrv['host']);
			if ($client > $csrv['limitfd']) {
				$list[] = self::hls($evtid, $studio['userid']);
			} else {
				$list   = $csrv['list'];
			}
			return $list;
		}
		//如果缓存中没有取到信息的话通过DB获取
		$lptops= self::get_casual_source_origin($studio['rtmp']);
		if (!$lptops) {
			return false;
		}
		$rorm  = C\model::load('pro.inc.rtmp');
		$where = array('status'=>1, 'stype'=>6, 'region'=>$areac);
		$data  = $rorm->get_list(0, -1, $where, 'id,pid,rtmp,host,ports,limitfd');
		if (!$data) {
			return false;
		}
		//随机分配一台服务器Copy镜像服务器
		$list  = array();
		$idx   = config::$nowtime % count($data);
		$client= (int)$redis->get('state_'.$data[$idx]['host']);
		if ($client > $data[$idx]['limitfd']) {
			$list[] = self::hls($evtid, $studio['userid']);
		} else {
			$ports = @explode(',', $data[$idx]['ports']);
			foreach ($ports as $port) {
				$clip  = sprintf('%s:%d', $data[$idx]['rtmp'], $port);
				$list[]= sprintf(self::copy_uri, $clip, $studio['rtocken'], $studio['id'], $lptops, $studio['userid'], $evtid).$segs;
			}
		}
		$cdata = array('limitfd'=>$data[$idx]['limitfd'], 'host'=>$data[$idx]['host'], 'list'=>$list);
		//缓存直播场次的分配情况，缓存一天时间
		$redis->set($ckey, $cdata, 86400);
		return $list;
	}

	/**
	 * 通过HOST获取地址信息
	 * @param unknown $evtid
	 * @param unknown $segs
	 * @param unknown $studio
	 * @return string[]
	 */
	private static function get_casual_host_recv($evtid, $segs, &$studio) {
		$list   = array();
		$rtmp   = parse_url($studio['rtmp']);
		$where  = array('status'=>1, 'stype'=>2, 'rtmp'=>$rtmp['host']);
		$data   = C\model::load('pro.inc.rtmp')->get_item($where, 'rtmp,host,ports,limitfd');
		if ($data) {
			$redis  = \classlib\cache::inst('redis');
			$ckey   = 'state_'.$data['host'];
			$client = (int)$redis->get($ckey);
			if ($client > $data['limitfd']) {
				$list[] = self::hls($evtid, $studio['userid']);
			} else {
				$ports  = @explode(',', $data['ports']);
				foreach ($ports as $port) {
					$clip  = sprintf('%s:%d', $data['rtmp'], $port);
					$list[] = sprintf(self::live_uri, $clip, $studio['rtocken'], $studio['id'], $studio['userid'], $evtid).$segs;
				}
			}
		}
		return $list;
	}

	/**
	 * 获取拉取优质流信息地址
	 * @param unknown $studio
	 * @param unknown $evtid
	 * @param string $segs
	 * @return string[]
	 */
	private static function get_original_recv(&$studio, $evtid, $segs='_001') {
		$areac= self::get_area_code();
		$studio['forward'] = intval($studio['forward']);
		//定位转发区域没有流的话直接拉取当前区域
		if ($studio['region'] > 0 && ($areac & $studio['forward']) == 0) {
			$areac = $studio['region'];
		}
		$list = array();
		$data = self::__rtmp(1, $areac);
		if ($data) {//随机分配一台流媒体服务器拉流
			$idx   = config::$nowtime % count($data);
			$ports = @explode(',', $data[$idx]['ports']);
			foreach ($ports as $port) {
				$clip  = sprintf('%s:%d', $data[$idx]['rtmp'], $port);
				$list[]= sprintf(self::live_uri, $clip, $studio['rtocken'], $studio['id'], $studio['userid'], $evtid).$segs;
			}
		}
		return $list;
	}

	/**
	 * 获取推流地址信息
	 * @param unknown $tocken
	 * @param unknown $sid
	 * @param unknown $userid
	 * @param unknown $evtid
	 * @return string
	 */
	public static function recv($sid, $evtid) {
		$list   = array();
		$studio = C\model::load('pro.pl.studio')->get_one($sid);
		//主播没有定位的话默认到北美
		($studio['region'] < 1) && $studio['region'] = self::area_def;
		if ($studio['iscasual'] == 0) {//获取优质主播的拉流地址
			$list = self::get_original_recv($studio, $evtid);
		} else {
			$list = self::get_casual_recv($studio, $evtid);
		}
		return $list;
	}
}
